﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Strategia.GameModels
{
    /// <summary>
    /// Geometric primitive class for drawing cubes.
    /// </summary>
    public class CubePrimitive
    {
        public CubeDescription cubeDesc;

        public Vector3 Position;
        public Vector3 Rotation { get; set; }
        public Vector3 Scale;
        public Vector3 Size { get; set; }

        public Vector3 Rozmiar;
        public bool CieploStale = true;
        public bool DrawMe = true;

        public float Temperature;

        public BoundingBox boundingBox;

        public Color color;

        private GraphicsDevice graphicsDevice;

        BasicEffect lineShader;
        VertexPositionColor[] points;
        short[] index;

        /// <summary>
        /// Constructs a new cube primitive, using default settings.
        /// </summary>
        public CubePrimitive(CubeDescription _cubeDesc, Vector3 Position, Vector3 Rotation, Vector3 Scale, Color color, byte alpha, GraphicsDevice graphicsDevice)
        {
            this.cubeDesc = _cubeDesc;

            this.Position = Position;
            this.Rotation = Rotation;
            this.Scale = Scale;

            this.color = color;
            this.color.A = alpha;

            Vector3 min = new Vector3((-0.5f * Scale.X) + Position.X, (-0.5f * Scale.Y) + Position.Y, (-0.5f * Scale.Z) + Position.Z);
            Vector3 max = new Vector3((0.5f * Scale.X) + Position.X, (0.5f * Scale.Y) + Position.Y, (0.5f * Scale.Z) + Position.Z);

            this.boundingBox = new BoundingBox(min, max);

            this.graphicsDevice = graphicsDevice;
        }

        public void UpdateBBox()
        {
            Vector3 min = new Vector3((-0.5f * Scale.X) + Position.X, (-0.5f * Scale.Y) + Position.Y, (-0.5f * Scale.Z) + Position.Z);
            Vector3 max = new Vector3((0.5f * Scale.X) + Position.X, (0.5f * Scale.Y) + Position.Y, (0.5f * Scale.Z) + Position.Z);

            this.boundingBox = new BoundingBox(min, max);
        }

        protected void DrawLines(int primativeCount, Matrix View, Matrix Projection)
        {
            if (lineShader == null)
                lineShader = new BasicEffect(graphicsDevice);

            lineShader.World = Matrix.Identity;
            lineShader.View = View;
            lineShader.Projection = Projection;
            lineShader.VertexColorEnabled = true;

            lineShader.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.LineList, points, 0, points.Length, index, 0, primativeCount);
        }

        public void DrawBox(Matrix View, Matrix Projection)
        {
            BuildBox(this.boundingBox, Color.Green);
            DrawLines(12, View, Projection);
        }

        protected void BuildBox(BoundingBox box, Color lineColor)
        {
            points = new VertexPositionColor[8];

            Vector3[] corners = box.GetCorners();

            points[0] = new VertexPositionColor(corners[1], lineColor); // Front Top Right
            points[1] = new VertexPositionColor(corners[0], lineColor); // Front Top Left
            points[2] = new VertexPositionColor(corners[2], lineColor); // Front Bottom Right
            points[3] = new VertexPositionColor(corners[3], lineColor); // Front Bottom Left
            points[4] = new VertexPositionColor(corners[5], lineColor); // Back Top Right
            points[5] = new VertexPositionColor(corners[4], lineColor); // Back Top Left
            points[6] = new VertexPositionColor(corners[6], lineColor); // Back Bottom Right
            points[7] = new VertexPositionColor(corners[7], lineColor); // Bakc Bottom Left

            index = new short[] {
	            0, 1, 0, 2, 1, 3, 2, 3,
	            4, 5, 4, 6, 5, 7, 6, 7,
	            0, 4, 1, 5, 2, 6, 3, 7
                };
        }

        /// <summary>
        /// Draws the primitive model, using the specified effect. Unlike the other
        /// Draw overload where you just specify the world/view/projection matrices
        /// and color, this method does not set any renderstates, so you must make
        /// sure all states are set to sensible values before you call it.
        /// </summary>
        public void Draw(Effect effect)
        {
            this.graphicsDevice = effect.GraphicsDevice;

            // Set our vertex declaration, vertex buffer, and index buffer.
            graphicsDevice.SetVertexBuffer(cubeDesc.vertexBuffer);

            graphicsDevice.Indices = cubeDesc.indexBuffer;


            foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
            {
                effectPass.Apply();

                int primitiveCount = cubeDesc.indices.Count / 3;

                graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, cubeDesc.vertices.Count, 0, primitiveCount);

            }
        }

        public void Draw(Matrix view, Matrix projection, bool counter)
        {
            if (DrawMe)
            {
                if (!counter) graphicsDevice.RasterizerState = RasterizerState.CullClockwise;

                Matrix world = Matrix.CreateScale(Scale) * Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z) * Matrix.CreateTranslation(Position);

                // Set BasicEffect parameters.
                cubeDesc.basicEffect.World = world;
                cubeDesc.basicEffect.View = view;
                cubeDesc.basicEffect.Projection = projection;
                cubeDesc.basicEffect.DiffuseColor = color.ToVector3();
                cubeDesc.basicEffect.Alpha = color.A / 255.0f;

                GraphicsDevice device = cubeDesc.basicEffect.GraphicsDevice;
                device.DepthStencilState = DepthStencilState.Default;

                if (color.A < 255)
                {
                    // Set renderstates for alpha blended rendering.
                    device.BlendState = BlendState.AlphaBlend;
                }
                else
                {
                    // Set renderstates for opaque rendering.
                    device.BlendState = BlendState.Opaque;
                }

                // Draw the model, using BasicEffect.
                Draw(cubeDesc.basicEffect);

                if (!counter) graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
            }
        }
    }
}
